package cn.aiweiyi.qingjing.coupon.settlement.executor.impl;

import cn.aiweiyi.qingjing.coupon.comm.constant.CouponCategory;
import cn.aiweiyi.qingjing.coupon.comm.vo.CouponTemplateSDK;
import cn.aiweiyi.qingjing.coupon.comm.vo.GoodsInfo;
import cn.aiweiyi.qingjing.coupon.comm.vo.SettlementInfo;

import cn.aiweiyi.qingjing.coupon.settlement.executor.AbstractExecutor;
import cn.aiweiyi.qingjing.coupon.settlement.executor.RuleExecutor;
import cn.aiweiyi.qingjing.coupon.settlement.util.BigDecimalUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 满减 + 折扣优惠券结算规则执行器
 */
@Slf4j
@Component
public class ManJianZheKouExecutor extends AbstractExecutor implements RuleExecutor {
    /**
     * 规则类型标记
     *
     * @return
     */
    @Override
    public CouponCategory ruleConfig() {
        return CouponCategory.MANJIAN_ZHEKOU;
    }

    /**
     * 效验商品类型与优惠券是否匹配
     *
     * @param settlementInfo 用户传递的计算信息
     * @return
     */
    protected boolean isGoodsTypeSatisfy(SettlementInfo settlementInfo) {
        log.debug("检查商品类型和优惠券模板类型!");
        List<Integer> goodsType = settlementInfo.getGoodsInfos().stream()
                .map(GoodsInfo::getType).collect(Collectors.toList());
        List<Integer> templateGoodsType = new ArrayList<>();
        settlementInfo.getCouponAndTemplateInfos().forEach(ct -> {
            templateGoodsType.addAll(JSON.parseObject(
                    ct.getTemplate().getRule().getUsage().getGoodsType(),
                    List.class
            ));
        });
        // 如果想要使用多累优惠券，则必须要所有的商品类型都包含在内，即差集为空
        // 差集为空 = 每个元素都相同
        return CollectionUtils.isEmpty(CollectionUtils.subtract(goodsType, templateGoodsType));
    }

    /**
     * 当前的两张优惠券是否可以共用
     *
     * @param mj 结算信息中的满减模板信息
     * @param zk 结算信息中的折扣模板信息
     * @return 是否可用
     */
    @SuppressAjWarnings("all")
    private boolean isTemplateCanShared(SettlementInfo.CouponAndTemplateInfo mj,
                                        SettlementInfo.CouponAndTemplateInfo zk) {
        // 满减券的Key：模板的编码 + 模板Id
        String mjKey = mj.getTemplate().getKey()
                + String.format("%04d", mj.getTemplate().getId());
        // 折扣券的Key：模板的编码 + 模板id
        String zkKey = zk.getTemplate().getKey()
                + String.format("%04d", zk.getTemplate().getId());
        // 把满减券的Key添加到集合，再把可以和该满减券叠加的模板编号也都添加进去
        List<String> allSharedKeysForMj = new ArrayList<>();
        allSharedKeysForMj.add(mjKey);
        allSharedKeysForMj.addAll(JSON.parseObject(
                mj.getTemplate().getRule().getWeight(), List.class));
        // 折扣
        List<String> allSharedKeysForZk = new ArrayList<>();
        allSharedKeysForZk.add(zkKey);
        allSharedKeysForZk.addAll(JSON.parseObject(
                zk.getTemplate().getRule().getWeight(), List.class));
        return CollectionUtils.isSubCollection(Arrays.asList(mjKey, zkKey),
                allSharedKeysForMj) || CollectionUtils.isSubCollection(
                Arrays.asList(mjKey, zkKey), allSharedKeysForZk);
    }

    /**
     * 优惠券规则的计算
     *
     * @param settlementInfo 包含了选择的优惠券
     * @return 修正过的结算信息
     */
    @Override
    public SettlementInfo computeRule(SettlementInfo settlementInfo) {
        double goodsSum = retain2Decimals(goodsCostSum(settlementInfo.getGoodsInfos()));
        // 商品类型的效验
        SettlementInfo probability = processGoodsTypeNotSatisfy(settlementInfo, goodsSum);
        if (null != probability) {
            log.debug("满减和折扣优惠券模板信息与商品不匹配");
            return probability;
        }
        SettlementInfo.CouponAndTemplateInfo mj = null;
        SettlementInfo.CouponAndTemplateInfo zk = null;
        // 把结算信息中的每个模板，按类型（满减、折扣）信息取出
        for (SettlementInfo.CouponAndTemplateInfo ct : settlementInfo.getCouponAndTemplateInfos()) {
            if (CouponCategory.of(ct.getTemplate().getCategory()) == CouponCategory.MANJIAN) {
                mj = ct;
            } else {
                zk = ct;
            }
        }
        assert null != mj;
        assert null != zk;
        // 判断当前折扣优惠券和满减券如果不能共用，清空优惠券，返回商品原价
        if (!isTemplateCanShared(mj, zk)) {
            log.debug("当前满减和折扣优惠券不能共用!");
            settlementInfo.setCost(goodsSum);
            settlementInfo.setCouponAndTemplateInfos(Collections.emptyList());
            return settlementInfo;
        }
        List<SettlementInfo.CouponAndTemplateInfo> ctInfos = new ArrayList<>();
        double mjBase = (double) mj.getTemplate().getRule().getDiscount().getBase();
        double mjQuota = (double) mj.getTemplate().getRule().getDiscount().getQuota();
        // 最终的价格
        double targetSum = goodsSum;
        // 先计算满减
        if (targetSum >= mjBase) {
            targetSum -= mjQuota;
            ctInfos.add(mj);
        }
        // 再计算折扣
        double zkQuota = (double) zk.getTemplate().getRule().getDiscount().getQuota();
        targetSum = BigDecimalUtils.multiply(targetSum, zkQuota * 1.0 / 100);
        ctInfos.add(zk);
        settlementInfo.setCouponAndTemplateInfos(ctInfos);
        settlementInfo.setCost(retain2Decimals(targetSum > minCost() ? targetSum : minCost()));
        log.debug("使用满减+折扣券，使金额从{}减少到{}元", goodsSum, settlementInfo.getCost());
        return settlementInfo;
    }
}
